/* 
实现此蓝图
ajax({
    // method: "GET",
    url: "http://httpbin.org/get",
    data: { username: "admin", password: "123456" },
    onSuccess: (data) => box.innerText = `data=${data}`,
    onFail: err => box.innerText = `err=${JSON.stringify(err)}`
});
*/

/* 1.0 GET版 */
function ajax1(config) {
  // 创建XHR对象
  const xhr = new XMLHttpRequest();

  // 解构（structure de-structure）出各种用户配置
  let { method, url, onSuccess, onFail, data } = config;

  // 提取出用户传入的method 如果没有传 则默认使用GET
  method = method || "GET";

  /* 处理URL */
  /* GET请求时 将数据拼接为查询参数（searchParams/queryString） username=admin&password=123456 */
  if (method === "GET" && data) {
    // 将data对象转化为查询参数
    // 提前准备好查询参数串
    let searchParamsStr = "";
    // 遍历key-value
    for (let key in data) {
      // 拼接字符串 key1=value1&key2=value2
      searchParamsStr += `&${key}=${data[key]}`;
    }

    // 锯掉头部的& &key1=value1&key2=value2
    // 从1截取到末尾
    searchParamsStr = searchParamsStr.slice(1);

    // 查询参数拼接到url之后
    url += `?${searchParamsStr}`;
    console.log("url=", url);
  }

  // 打开通向目标地址的管道
  xhr.open(method, url);

  /* 配置回调函数 */
  xhr.onload = function () {
    // 回调用户配置的[成功回调] 入参网络数据
    onSuccess(xhr.responseText);
  };
  xhr.onerror = function (err) {
    // 回调用户配置的[失败回调] 入参错误信息
    onFail(err);
  };

  // 发送请求
  xhr.send(null);
}

/**
 * 将data对象转化为查询参数串 key1=value1&key2=value2
 * @param {Object} data
 * @returns 查询参数串
 */
function dataToSearchParamStr(data) {
  // 将data对象转化为查询参数
  // 提前准备好查询参数串
  let searchParamsStr = "";
  // 遍历key-value
  for (let key in data) {
    // 拼接字符串 key1=value1&key2=value2
    searchParamsStr += `&${key}=${data[key]}`;
  }

  // 锯掉头部的& &key1=value1&key2=value2
  // 从1截取到末尾
  searchParamsStr = searchParamsStr.slice(1);

  // 返回重构好的查询参数串
  return searchParamsStr;
}

/* 2.0 GET版+表单版+JSON版 */
function ajax(config) {
  // 创建XHR对象
  const xhr = new XMLHttpRequest();

  // 解构（structure de-structure）出各种用户配置
  let { method, url, onSuccess, onFail, data, dataType } = config;

  // 提取出用户传入的method 如果没有传 则默认使用GET
  method = method || "GET";

  // 只要POST请求 则请求体默认JSON
  ["POST", "PUT", "PATCH"].includes(method) && !dataType && (dataType = "json");

  // 准备好一个请求体
  let reqBody = null;

  /* 处理URL */
  /* GET请求时 将数据拼接为查询参数（searchParams/queryString） username=admin&password=123456 */
  if (method === "GET" && data) {
    // 查询参数拼接到url之后
    url += `?${dataToSearchParamStr(data)}`;
    console.log("url=", url);
  }

  // POST一个表单时
  else if (method === "POST" && dataType === "form") {
    // 请求体变成 username=admin&password=123456
    reqBody = dataToSearchParamStr(data);
  }

  // POST/PUT一个JSON
  else if (["POST", "PUT", "PATCH"].includes(method) && dataType === "json") {
    reqBody = JSON.stringify(data);
  }

  // 打开通向目标地址的管道
  xhr.open(method, url);

  /* 根据dataType设置请求头 */
  switch (true) {
    case dataType === "form":
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      break;
    case dataType === "json":
      xhr.setRequestHeader("Content-Type", "application/json");
      break;
    default:
      break;
  }

  /* 配置回调函数 */
  xhr.onload = function () {
    // 回调用户配置的[成功回调] 入参网络数据
    onSuccess(xhr.responseText);
  };
  xhr.onerror = function (err) {
    // 回调用户配置的[失败回调] 入参错误信息
    onFail(err);
  };

  // 发送请求 携带请求体
  xhr.send(reqBody);
}

/* 3.0 GET版+表单版+JSON版+处理异常 */
function ajax(config) {
  // 创建XHR对象
  const xhr = new XMLHttpRequest();

  // 解构（structure de-structure）出各种用户配置
  let { method, url, onSuccess, onFail, data, dataType, headers } = config;

  /* 如果用户是个小可爱 连URL都不给 [教育]它一下 */
  if (!url) {
    // 抛出错误（代码惨死于当前行）
    throw new Error("亲爱的萌萌哒:URL必须给哦亲");

    console.log(
      "执行到此处也可以到前台领取女朋友一个+现金100万+月薪30K offer一个"
    );
  }

  /* 如果用户没有给成功失败回调 帮用户给它一个默认值 */
  !onSuccess && (onSuccess = (data) => console.log("data=", data));
  !onFail && (onFail = (err) => console.error("err=", err));

  // 提取出用户传入的method 如果没有传 则默认使用GET
  method = method || "GET";

  // 只要POST请求 则请求体默认JSON
  ["POST", "PUT", "PATCH"].includes(method) && !dataType && (dataType = "json");

  // 准备好一个请求体
  let reqBody = null;

  /* 处理URL */
  /* GET请求时 将数据拼接为查询参数（searchParams/queryString） username=admin&password=123456 */
  if (method === "GET" && data) {
    // 查询参数拼接到url之后
    url += `?${dataToSearchParamStr(data)}`;
    // console.log("url=", url);
  }

  // POST一个表单时
  else if (method === "POST" && dataType === "form") {
    // 请求体变成 username=admin&password=123456
    reqBody = dataToSearchParamStr(data);
  }

  // POST/PUT一个JSON
  else if (["POST", "PUT", "PATCH"].includes(method) && dataType === "json") {
    reqBody = JSON.stringify(data);
  }

  // 打开通向目标地址的管道
  xhr.open(method, url);

  /* 根据dataType设置请求头 */
  switch (true) {
    case dataType === "form":
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      break;
    case dataType === "json":
      xhr.setRequestHeader("Content-Type", "application/json");
      break;
    default:
      break;
  }

  /* 设置其它请求头 */
  if (headers) {
    for (let name in headers) {
      xhr.setRequestHeader(name, headers[name]);
    }
  }

  /* 配置回调函数 */
  xhr.onload = function () {
    // 回调用户配置的[成功回调] 入参网络数据
    onSuccess(xhr.responseText);
  };
  xhr.onerror = function (err) {
    // 回调用户配置的[失败回调] 入参错误信息
    onFail(err);
  };

  // 发送请求 携带请求体
  xhr.send(reqBody);
}

/* 4.0 既能执行AJAX请求 又能返回Promise对象 */
function ajaxPromise(config) {
  // 返回一个执行AJAX的Promise对象
  return new Promise((resolve, reject) => {
    // ajax(config)
    ajax({
      ...config,

      // 请求成功时履约，就近回调then中的操作
      onSuccess: (data) => resolve(data),

      // 请求失败时毁约，就近回调catch中的操作
      onFail: (err) => reject(err),
    });
  });
}
